/**
 *  Copyright 2002-2009 the original author or authors.
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package foo.bar.wiki.security;

import org.apache.shiro.realm.Realm;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationException;

import java.net.InetAddress;
import java.util.Collection;

import foo.bar.wiki.plugins.PluginManager;
import foo.bar.wiki.plugins.PluginException;
import foo.bar.wiki.domain.User;

/**
 * An extended {@link org.apache.shiro.mgt.SecurityManager} that allows {@link Subject} that it
 * creates to have one principle ({@link UserAccountRealm#ANONYMOUS_USER_ACCOUNT} when it's an
 * anonymous user. 
 *
 * @author tmjee
 * @version $Date: 2009-03-14 14:53:07 +0800 (Sat, 14 Mar 2009) $ $Id$
 */
public class AnonymousSubjectAwareDefaultWebSecurityManager extends DefaultWebSecurityManager {

    private PluginManager pluginManager;

    public AnonymousSubjectAwareDefaultWebSecurityManager(Realm singleRealm, PluginManager pluginManager) {
        super(singleRealm);
        this.pluginManager = pluginManager;
    }

    public AnonymousSubjectAwareDefaultWebSecurityManager(Collection<Realm> realms, PluginManager pluginManager) {
        super(realms);
        this.pluginManager = pluginManager;
    }


    protected Subject createSubject(PrincipalCollection principals, Session existing,
                                    boolean authenticated, InetAddress inetAddress) {
        // when the principals is null or empty, that means the login was not successfull,
        // here we give it the anonymous account's principals.
        if(principals == null || principals.isEmpty()) {
            if (null == UserAccountRealm.ANONYMOUS_USER_ACCOUNT) {
                throw new RuntimeException("UserAccountRealm.ANONYMOUS_USER_ACCOUNT is null, this should be setup in SetupService, check if SetupService has been configured and started up properly");
            }
            return new AnonymousSubjectAwareDelegatingSubject(
                            UserAccountRealm.ANONYMOUS_USER_ACCOUNT.getPrincipals(),
                            authenticated, inetAddress, existing, this);
        }
        return new AnonymousSubjectAwareDelegatingSubject(principals, authenticated, inetAddress, existing, this);
    }


    protected void onSuccessfulLogin(AuthenticationToken token, AuthenticationInfo info, Subject subject) {
        User user = (User) info.getPrincipals().iterator().next();
        try {
            pluginManager.getUserActivityListenerPlugin().preUserLogin(user);
            super.onSuccessfulLogin(token, info, subject);
            pluginManager.getUserActivityListenerPlugin().postUserLogin(user);
        }
        catch(PluginException e) {
            throw new AuthenticationException(e);    
        }
    }

    protected void onFailedLogin(AuthenticationToken token, AuthenticationException ae, Subject subject) {
        super.onFailedLogin(token, ae, subject);
        pluginManager.getUserActivityListenerPlugin().userLoginFailed(token.getPrincipal().toString());
    }

    protected void beforeLogout(Subject subject) {
        super.beforeLogout(subject);
        pluginManager.getUserActivityListenerPlugin().userLogout((User)subject.getPrincipals().iterator().next());
    }
}
